﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
using Vanara.Extensions;
using static Vanara.PInvoke.ComCtl32;
using static Vanara.PInvoke.Macros;
using static Vanara.PInvoke.User32;
using ComboBoxStyle = System.Windows.Forms.ComboBoxStyle;

namespace Vanara.Windows.Forms
{
	internal interface IPopupControlHost
	{
		/// <summary>
		/// Hides drop-down area of combo box, if shown.
		/// </summary>
		void HideDropDown();

		/// <summary>
		/// Displays drop-down area of combo box, if not already shown.
		/// </summary>
		void ShowDropDown();
	}

	/// <summary>
	/// <c>CustomComboBox</c> is an extension of <see cref="ComboBox"/> which provides drop-down customization.
	/// </summary>
	public abstract class CustomComboBox : ComboBox, IPopupControlHost
	{
		private const int maxItemLen = 1024;
		private static DateTime showTime = DateTime.Now;

		/// <summary>Actual drop-down control itself.</summary>
		private Control dropDownCtrl;

		/// <summary>Indicates if drop-down is currently shown.</summary>
		private bool droppedDown;
		/// <summary>Time drop-down was last hidden.</summary>
		private DateTime lastHideTime = DateTime.Now;

		/// <summary>Popup control.</summary>
		private PopupControl popupCtrl = new PopupControl();

		/// <summary>Original size of combo box drop-down when first assigned.</summary>
		private Size sizeCombo;

		/// <summary>Indicates current sizing mode.</summary>
		private SizeMode sizeMode = SizeMode.UseComboSize;

		/// <summary>Original size of control dimensions when first assigned.</summary>
		private Size sizeOriginal = new Size(1, 1);

		/// <summary>Automatic focus timer helps make sure drop-down control is focused for user input upon drop-down.</summary>
		private Timer timerAutoFocus;

		private ToolTip toolTip;

		/// <summary>
		/// Initializes a new instance of the <see cref="CustomComboBox"/> class.
		/// </summary>
		public CustomComboBox()
		{
			base.DropDownStyle = ComboBoxStyle.DropDownList;
			base.Items.Add(String.Empty);
			sizeCombo = new Size(base.DropDownWidth, base.DropDownHeight);
			popupCtrl.Closing += DropDownClosing;
			toolTip = new ToolTip { StripAmpersands = true };
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CustomComboBox"/> class.
		/// </summary>
		/// <param name="dropControl">The control to display in the drop-down.</param>
		public CustomComboBox(Control dropControl) : this()
		{
			DropDownControl = dropControl;
		}

		/// <summary>
		/// Occurs when the drop-down portion of a <see cref="CustomComboBox"/> is shown.
		/// </summary>
		[Category("Action")]
		public new event EventHandler DropDown;

		/// <summary>
		/// Occurs when the drop-down portion of the <see cref="CustomComboBox"/> is no longer visible.
		/// </summary>
		[Category("Action")]
		public new event EventHandler DropDownClosed;

		/// <summary>
		/// Sizing mode for the CustomComboBox drop-down area.
		/// </summary>
		public enum SizeMode
		{
			/// <summary>
			/// Uses the width of the parent.
			/// </summary>
			UseComboSize,

			/// <summary>
			/// Uses the width of the supplied control for the drop-down.
			/// </summary>
			UseControlSize,

			/// <summary>
			/// Uses the width of the combo box drop-down box.
			/// </summary>
			UseDropDownSize
		}

		internal enum GripAlignMode
		{
			TopLeft,
			TopRight,
			BottomLeft,
			BottomRight
		}

		internal enum PopupResizeMode
		{
			None = 0,

			// Individual styles.
			Left = 1,

			Top = 2,
			Right = 4,
			Bottom = 8,

			// Combined styles.
			All = (Top | Left | Bottom | Right),

			TopLeft = (Top | Left),
			TopRight = (Top | Right),
			BottomLeft = (Bottom | Left),
			BottomRight = (Bottom | Right)
		}

		/// <summary>
		/// Indicates if drop-down is re-sizable.
		/// </summary>
		[Category("Custom Drop-Down"), Description("Indicates if drop-down is re-sizable."), DefaultValue(true)]
		public bool AllowResizeDropDown { get; set; } = true;

		/// <summary>
		/// Gets or sets the size of the drop-down control itself.
		/// </summary>
		/// <value>The size of the drop-down control.</value>
		[Category("Custom Drop-Down"), Browsable(false)]
		public Size ControlSize
		{
			get => sizeOriginal; set
			{
				sizeOriginal = value;
				if (DropDownSizeMode == SizeMode.UseControlSize)
					AutoSizeDropDown();
			}
		}

		/// <summary>
		/// Gets or sets the property to display for this <see cref="T:System.Windows.Forms.ListControl"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// A <see cref="T:System.String"/> specifying the name of an object property that is contained in the collection specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property. The default is an empty string ("").
		/// </returns>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new string DisplayMember { get => base.DisplayMember; set { } }

		/// <summary>
		/// Gets or sets drop-down control itself.
		/// </summary>
		/// <value>The drop down control.</value>
		[Browsable(false)]
		public Control DropDownControl { get => dropDownCtrl; set => AssignControl(value); }

		/// <summary>
		/// Gets or sets the height in pixels of the drop-down portion of the <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The height, in pixels, of the drop-down box.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The specified value is less than one.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int DropDownHeight { get => base.DropDownHeight; set { } }

		/// <summary>
		/// Gets or sets the sizing mode for the drop-down.
		/// </summary>
		/// <value>The drop down size mode.</value>
		[Category("Custom Drop-Down"), Description("Indicates current sizing mode."), DefaultValue(SizeMode.UseComboSize)]
		public SizeMode DropDownSizeMode
		{
			get => sizeMode; set
			{
				if (value != sizeMode)
				{
					sizeMode = value;
					AutoSizeDropDown();
				}
			}
		}

		/// <summary>
		/// Gets or sets a value specifying the style of the combo box.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// One of the <see cref="T:System.Windows.Forms.ComboBoxStyle"/> values. The default is DropDown.
		/// </returns>
		/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
		/// The assigned value is not one of the <see cref="T:System.Windows.Forms.ComboBoxStyle"/> values.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new ComboBoxStyle DropDownStyle { get => base.DropDownStyle; set { } }

		/// <summary>
		/// Gets or sets the width of the of the drop-down portion of a combo box.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The width, in pixels, of the drop-down box.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The specified value is less than one.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int DropDownWidth { get => base.DropDownWidth; set { } }

		/// <summary>
		/// Gets or sets the size of the drop-down area.
		/// </summary>
		/// <value>The size of the drop-down area.</value>
		[Category("Custom Drop-Down")]
		public Size DropSize
		{
			get => sizeCombo; set
			{
				sizeCombo = value;
				if (DropDownSizeMode == SizeMode.UseDropDownSize)
					AutoSizeDropDown();
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether the control should resize to avoid showing partial items.
		/// </summary>
		/// <value></value>
		/// <returns>true if the list portion can contain only complete items; otherwise, false. The default is true.
		/// </returns>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new bool IntegralHeight { get => base.IntegralHeight; set { } }

		/// <summary>
		/// Indicates if drop-down is currently shown.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is dropped down; otherwise, <c>false</c>.
		/// </value>
		[Browsable(false)]
		public bool IsDroppedDown => droppedDown /*&& m_popupCtrl.Visible*/;

		/// <summary>
		/// Gets or sets the height of an item in the combo box.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The height, in pixels, of an item in the combo box.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The item height value is less than zero.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int ItemHeight { get => base.ItemHeight; set { } }

		/// <summary>
		/// Gets an object representing the collection of the items contained in this <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// A <see cref="T:System.Windows.Forms.ComboBox.ObjectCollection"/> representing the items in the <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </returns>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new ObjectCollection Items => base.Items;

		/// <summary>
		/// Gets or sets the maximum number of items to be shown in the drop-down portion of the <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The maximum number of items of in the drop-down portion. The minimum for this property is 1 and the maximum is 100.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The maximum number is set less than one or greater than 100.
		/// </exception>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int MaxDropDownItems { get => base.MaxDropDownItems; set { } }

		/// <summary>
		/// Gets or sets the index specifying the currently selected item.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// A zero-based index of the currently selected item. A value of negative one (-1) is returned if no item is selected.
		/// </returns>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// The specified index is less than or equal to -2.
		/// -or-
		/// The specified index is greater than or equal to the number of items in the combo box.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int SelectedIndex { get => base.SelectedIndex; set { } }

		/// <summary>
		/// Gets or sets currently selected item in the <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The object that is the currently selected item or null if there is no currently selected item.
		/// </returns>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new object SelectedItem { get => base.SelectedItem; set { } }

		/// <summary>
		/// Gets or sets the text that is selected in the editable portion of a <see cref="T:System.Windows.Forms.ComboBox"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// A string that represents the currently selected text in the combo box. If <see cref="P:System.Windows.Forms.ComboBox.DropDownStyle"/> is set to <see cref="F:System.Windows.Forms.ComboBoxStyle.DropDownList"/>, the return value is an empty string ("").
		/// </returns>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new string SelectedText { get => base.SelectedText; set { } }

		/// <summary>
		/// Gets or sets the value of the member property specified by the <see cref="P:System.Windows.Forms.ListControl.ValueMember"/> property.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// An object containing the value of the member of the data source specified by the <see cref="P:System.Windows.Forms.ListControl.ValueMember"/> property.
		/// </returns>
		/// <exception cref="T:System.InvalidOperationException">
		/// The assigned value is null or the empty string ("").
		/// </exception>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new object SelectedValue { get => base.SelectedValue; set { } }

		/// <summary>
		/// Gets or sets the number of characters selected in the editable portion of the combo box.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The number of characters selected in the combo box.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The value was less than zero.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int SelectionLength { get => base.SelectionLength; set { } }

		/// <summary>
		/// Gets or sets the starting index of text selected in the combo box.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The zero-based index of the first character in the string of the current text selection.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The value is less than zero.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new int SelectionStart { get => base.SelectionStart; set { } }

		/// <summary>
		/// Gets or sets a value indicating whether the items in the combo box are sorted.
		/// </summary>
		/// <value></value>
		/// <returns>true if the combo box is sorted; otherwise, false. The default is false.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// An attempt was made to sort a <see cref="T:System.Windows.Forms.ComboBox"/> that is attached to a data source.
		/// </exception>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new bool Sorted { get => base.Sorted; set { } }

		/// <summary>
		/// Gets or sets the text associated with this control.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The text associated with this control.
		/// </returns>
		/// <PermissionSet>
		/// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
		/// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
		/// </PermissionSet>
		public new string Text
		{
			get => base.Items.Count == 0 ? string.Empty : base.Items[0].ToString(); set
			{
				// Get toolTip value
				var items = value.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
				if (items.Length > 0)
					toolTip.SetToolTip(this, string.Join("\r\n", items));
				// Limit length and then set 0 item to value so it displays
				if (value.Length > maxItemLen)
					value = value.Substring(0, maxItemLen);
				if (base.Items.Count == 0)
					base.Items.Add(value);
				else if (!Equals(value, base.Items[0]))
					base.Items[0] = value;
				base.SelectedIndex = 0;
			}
		}

		/// <summary>
		/// Gets or sets the property to use as the actual value for the items in the <see cref="T:System.Windows.Forms.ListControl"/>.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// A <see cref="T:System.String"/> representing the name of an object property that is contained in the collection specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property. The default is an empty string ("").
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// The specified property cannot be found on the object specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property.
		/// </exception>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false), ReadOnly(true)]
		public new string ValueMember { get => base.ValueMember; set { } }

		/// <summary>
		/// Gets or sets a value indicating whether to prevent hiding the popup window.
		/// </summary>
		/// <value><c>true</c> if hiding is prevented; otherwise, <c>false</c>.</value>
		protected bool PreventPopupHide { get; set; }

		/// <summary>
		/// Hides drop-down area of combo box, if shown.
		/// </summary>
		public virtual void HideDropDown()
		{
			if (popupCtrl != null && IsDroppedDown && !PreventPopupHide)
			{
				// Hide drop-down control.
				popupCtrl.Hide();
				droppedDown = false;

				// Disable automatic focus timer.
				if (timerAutoFocus != null && timerAutoFocus.Enabled)
					timerAutoFocus.Enabled = false;

				Focus();

				// Raise drop-down closed event.
				OnDropDownClosed(EventArgs.Empty);
			}
		}

		/// <summary>
		/// Preprocesses keyboard or input messages within the message loop before they are dispatched.
		/// </summary>
		/// <param name="m">A <see cref="Message"/>, passed by reference, that represents the message to process. The possible values are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR.</param>
		/// <returns><c>true</c> if the message was processed by the control; otherwise, <c>false</c>.</returns>
		public override bool PreProcessMessage(ref Message m)
		{
			if (m.Msg == (int)WindowMessage.WM_REFLECT + (int)WindowMessage.WM_COMMAND)
			{
				if (HIWORD(m.WParam) == (int)ComboBoxNotification.CBN_DROPDOWN)
					return false;
			}
			return base.PreProcessMessage(ref m);
		}

		/// <summary>
		/// Displays drop-down area of combo box, if not already shown.
		/// </summary>
		public virtual void ShowDropDown()
		{
			if (popupCtrl != null && !IsDroppedDown)
			{
				// Raise drop-down event.
				OnDropDown(EventArgs.Empty);

				// Restore original control size.
				AutoSizeDropDown();

				var location = PointToScreen(new Point(0, Height));

				// Actually show popup.
				var resizeMode = (AllowResizeDropDown ? PopupResizeMode.BottomRight : PopupResizeMode.None);
				popupCtrl.Show(DropDownControl, location.X, location.Y, Width, Height, resizeMode);
				droppedDown = true;

				popupCtrl.PopupControlHost = this;

				// Initialize automatic focus timer?
				if (timerAutoFocus == null)
				{
					timerAutoFocus = new Timer {Interval = 10};
					timerAutoFocus.Tick += timerAutoFocus_Tick;
				}
				// Enable the timer!
				timerAutoFocus.Enabled = true;
				showTime = DateTime.Now;
			}
		}

		/// <summary>
		/// Assigns control to custom drop-down area of combo box.
		/// </summary>
		/// <param name="control">Control to be used as drop-down. Please note that this control must not be contained elsewhere.</param>
		protected virtual void AssignControl(Control control)
		{
			// If specified control is different then...
			if (control != dropDownCtrl)
			{
				if (dropDownCtrl != null)
					Controls.Remove(dropDownCtrl);

				// Reference the user-specified drop down control.
				dropDownCtrl = control;

				// Preserve original container size.
				sizeOriginal = dropDownCtrl?.Size ?? new Size(1, 1);

				if (dropDownCtrl != null)
					Controls.Add(control);
			}
		}

		/// <summary>
		/// Automatically resize drop-down from properties.
		/// </summary>
		protected void AutoSizeDropDown()
		{
			if (DropDownControl != null)
			{
				switch (DropDownSizeMode)
				{
					case SizeMode.UseComboSize:
						DropDownControl.Size = new Size(Width, sizeCombo.Height);
						break;

					case SizeMode.UseControlSize:
						DropDownControl.Size = new Size(sizeOriginal.Width, sizeOriginal.Height);
						break;

					case SizeMode.UseDropDownSize:
						DropDownControl.Size = sizeCombo;
						break;
				}
			}
		}

		/// <summary>
		/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.ComboBox"/> and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (timerAutoFocus != null)
				{
					timerAutoFocus.Dispose();
					timerAutoFocus = null;
				}
			}
			base.Dispose(disposing);
		}

		/// <summary>
		/// Raises the <see cref="DropDown"/> event.
		/// </summary>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected new virtual void OnDropDown(EventArgs args)
		{
			DropDown?.Invoke(this, args);
		}

		/// <summary>
		/// Raises the <see cref="DropDownClosed"/> event.
		/// </summary>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected new virtual void OnDropDownClosed(EventArgs args)
		{
			DropDownClosed?.Invoke(this, args);
		}

		/// <summary>
		/// Raises the <see cref="E:FontChanged"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
		protected override void OnFontChanged(EventArgs e)
		{
			base.OnFontChanged(e);
			if (DropDownControl != null)
				DropDownControl.Font = Font;
		}

		/// <summary>
		/// Processes Windows messages.
		/// </summary>
		/// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
		protected override void WndProc(ref Message m)
		{
			if (m.Msg == (int)WindowMessage.WM_LBUTTONDOWN && DropDownControl != null)
			{
				AutoDropDown();
				return;
			}

			if (m.Msg == (int)WindowMessage.WM_REFLECT + (int)WindowMessage.WM_COMMAND && DropDownControl != null)
			{
				switch (HIWORD(m.WParam))
				{
					case (int)ComboBoxNotification.CBN_DROPDOWN:
						AutoDropDown();
						return;

					case (int)ComboBoxNotification.CBN_CLOSEUP:
						if ((DateTime.Now - showTime).Seconds > 1)
							HideDropDown();
						return;
				}
			}

			base.WndProc(ref m);
		}

		private void AutoDropDown()
		{
			if (popupCtrl != null && popupCtrl.Visible)
				HideDropDown();
			else if ((DateTime.Now - lastHideTime).Milliseconds > 50)
				ShowDropDown();
		}

		private void DropDownClosing(object sender, ToolStripDropDownClosingEventArgs e)
		{
			lastHideTime = DateTime.Now;
		}

		private void m_dropDown_LostFocus(object sender, EventArgs e)
		{
			lastHideTime = DateTime.Now;
		}

		private void timerAutoFocus_Tick(object sender, EventArgs e)
		{
			if (popupCtrl != null && popupCtrl.Visible && !DropDownControl.Focused)
			{
				DropDownControl.Focus();
				timerAutoFocus.Enabled = false;
			}

			if (DroppedDown)
				DroppedDown = false;
		}

		internal static class GripRenderer
		{
			private static Bitmap gripBitmap;

			private static Bitmap GripBitmap => gripBitmap;

			public static void RefreshSystemColors(Graphics g, Size size)
			{
				InitializeGripBitmap(g, size, true);
			}

			public static void Render(Graphics g, Point location, Size size, GripAlignMode mode)
			{
				InitializeGripBitmap(g, size, false);

				// Calculate display size and position of grip.
				switch (mode)
				{
					case GripAlignMode.TopLeft:
						size.Height = -size.Height;
						size.Width = -size.Width;
						break;

					case GripAlignMode.TopRight:
						size.Height = -size.Height;
						break;

					case GripAlignMode.BottomLeft:
						size.Width = -size.Height;
						break;
				}

				// Reverse size grip for left-aligned.
				if (size.Width < 0)
					location.X -= size.Width;
				if (size.Height < 0)
					location.Y -= size.Height;

				g.DrawImage(GripBitmap, location.X, location.Y, size.Width, size.Height);
			}

			public static void Render(Graphics g, Point location, GripAlignMode mode)
			{
				Render(g, location, new Size(16, 16), mode);
			}

			private static void InitializeGripBitmap(Graphics g, Size size, bool forceRefresh)
			{
				if (gripBitmap == null || forceRefresh || size != gripBitmap.Size)
				{
					// Draw size grip into a bitmap image.
					gripBitmap = new Bitmap(size.Width, size.Height, g);
					using (var gripG = Graphics.FromImage(gripBitmap))
						ControlPaint.DrawSizeGrip(gripG, SystemColors.ButtonFace, 0, 0, size.Width, size.Height);
				}
			}
		}

		internal class PopupControl
		{
			private PopupDropDown dropDown;
			private ToolStripControlHost host;

			public PopupControl()
			{
				InitializeDropDown();
			}

			public event ToolStripDropDownClosingEventHandler Closing { add { dropDown.Closing += value; } remove { dropDown.Closing -= value; } }

			public bool AutoResetWhenClosed { get; set; } = false;

			public Control Control => host?.Control;

			public Padding Margin { get; set; } = new Padding(1);

			public Padding Padding { get; set; } = Padding.Empty;

			/// <summary>
			/// Gets or sets the popup control host, this is used to hide/show popup.
			/// </summary>
			public IPopupControlHost PopupControlHost { get; set; }

			public bool Visible => (dropDown != null && dropDown.Visible) ? true : false;

			public void Hide()
			{
				if (dropDown != null && dropDown.Visible)
				{
					dropDown.Hide();
					DisposeHost();
				}
			}

			public void Reset()
			{
				DisposeHost();
			}

			public void Show(Control control, int x, int y, PopupResizeMode resizeMode = PopupResizeMode.None)
			{
				Show(control, x, y, -1, -1, resizeMode);
			}

			public void Show(Control control, int x, int y, int width, int height, PopupResizeMode resizeMode)
			{
				var controlSize = control.Size;

				InitializeHost(control);

				dropDown.ResizeMode = resizeMode;
				dropDown.Show(x, y, width, height);

				control.Focus();
			}

			protected void DisposeHost()
			{
				if (host != null)
				{
					// Make sure host is removed from drop down.
					dropDown?.Items.Clear();

					// Dispose of host.
					host = null;
				}

				PopupControlHost = null;
			}

			protected void InitializeDropDown()
			{
				// Does a drop down exist?
				if (dropDown == null)
				{
					dropDown = new PopupDropDown(false);
					dropDown.Closed += DropDownClosed;
				}
			}

			protected void InitializeHost(Control control)
			{
				InitializeDropDown();

				// If control is not yet being hosted then initialize host.
				if (control != Control)
					DisposeHost();

				// Create a new host?
				if (host == null)
				{
					host = new ToolStripControlHost(control)
					{
						AutoSize = false,
						Padding = Padding,
						Margin = Margin
					};
				}

				// Add control to drop-down.
				dropDown.Items.Clear();
				dropDown.Padding = dropDown.Margin = Padding.Empty;
				dropDown.Items.Add(host);
			}

			private void DropDownClosed(object sender, ToolStripDropDownClosedEventArgs e)
			{
				if (AutoResetWhenClosed)
					DisposeHost();

				// Hide drop down within popup control.
				PopupControlHost?.HideDropDown();
			}
		}

		internal class PopupDropDown : ToolStripDropDown
		{
			private bool lockedHostedControlSize;
			private bool lockedThisSize;
			private bool refreshSize;
			private PopupResizeMode resizeMode = PopupResizeMode.None;

			public PopupDropDown(bool autoSize)
			{
				AutoSize = autoSize;
				Padding = Margin = Padding.Empty;
			}

			/// <summary>
			/// Type of resize mode, grips are automatically drawn at bottom-left and bottom-right corners.
			/// </summary>
			public PopupResizeMode ResizeMode
			{
				get => resizeMode; set
				{
					if (value != resizeMode)
					{
						resizeMode = value;
						Invalidate();
					}
				}
			}

			/// <summary>
			/// Bounds of active grip box position.
			/// </summary>
			protected Rectangle GripBounds { get; set; } = Rectangle.Empty;

			/// <summary>
			/// Indicates when a grip box is shown.
			/// </summary>
			protected bool IsGripShown => (ResizeMode == PopupResizeMode.TopLeft || ResizeMode == PopupResizeMode.TopRight ||
				ResizeMode == PopupResizeMode.BottomLeft || ResizeMode == PopupResizeMode.BottomRight);

			public bool CompareResizeMode(PopupResizeMode resizeMode) => (ResizeMode & resizeMode) == resizeMode;

			public Control GetHostedControl()
			{
				if (Items.Count > 0)
				{
					var host = Items[0] as ToolStripControlHost;
					if (host != null)
						return host.Control;
				}
				return null;
			}

			/// <summary>
			/// Processes the resizing messages.
			/// </summary>
			/// <param name="m">The message.</param>
			/// <returns>true, if the WndProc method from the base class shouldn't be invoked.</returns>
			public bool ProcessGrip(ref Message m) => ProcessGrip(ref m, true);

			public void Show(int x, int y, int width = -1, int height = -1)
			{
				// If no hosted control is associated, this procedure is pointless!
				var hostedControl = GetHostedControl();
				if (hostedControl == null)
					return;

				// Initially hosted control should be displayed within a drop down of 1x1, however
				// its size should exceed the dimensions of the drop-down.
				{
					lockedHostedControlSize = true;
					lockedThisSize = true;

					// Display actual popup and occupy just 1x1 pixel to avoid automatic reposition.
					Size = new Size(1, 1);
					base.Show(x, y);

					lockedHostedControlSize = false;
					lockedThisSize = false;
				}

				// Resize drop-down to fit its contents.
				ResizeFromContent(width);

				// If client area was enlarged using the minimum width parameter, then the hosted
				// control must also be enlarged.
				if (refreshSize)
					RecalculateHostedControlLayout();

				// If popup is overlapping the initial position then move above!
				if (y > Top && y <= Bottom)
				{
					Top = y - Height - (height != -1 ? height : 0);

					var previous = ResizeMode;
					if (ResizeMode == PopupResizeMode.BottomLeft)
						ResizeMode = PopupResizeMode.TopLeft;
					else if (ResizeMode == PopupResizeMode.BottomRight)
						ResizeMode = PopupResizeMode.TopRight;

					if (ResizeMode != previous)
						RecalculateHostedControlLayout();
				}

				// Assign event handler to control.
				hostedControl.SizeChanged += hostedControl_SizeChanged;
			}

			protected static int HIWORD(int n) => (n >> 16) & 0xffff;

			protected static int HIWORD(IntPtr n) => HIWORD(unchecked((int)(long)n));

			protected static int LOWORD(int n) => n & 0xffff;

			protected static int LOWORD(IntPtr n) => LOWORD(unchecked((int)(long)n));

			protected void hostedControl_SizeChanged(object sender, EventArgs e)
			{
				// Only update size of this container when it is not locked.
				if (!lockedHostedControlSize)
					ResizeFromContent(-1);
			}

			protected override void OnClosing(ToolStripDropDownClosingEventArgs e)
			{
				var hostedControl = GetHostedControl();
				if (hostedControl != null)
					hostedControl.SizeChanged -= hostedControl_SizeChanged;
				base.OnClosing(e);
			}

			protected override void OnPaint(PaintEventArgs e)
			{
				base.OnPaint(e);

				GripBounds = Rectangle.Empty;

				if (CompareResizeMode(PopupResizeMode.BottomLeft))
				{
					// Draw grip area at bottom-left of popup.
					e.Graphics.FillRectangle(SystemBrushes.ButtonFace, 1, Height - 16, Width - 2, 14);
					GripBounds = new Rectangle(1, Height - 16, 16, 16);
					GripRenderer.Render(e.Graphics, GripBounds.Location, GripAlignMode.BottomLeft);
				}
				else if (CompareResizeMode(PopupResizeMode.BottomRight))
				{
					// Draw grip area at bottom-right of popup.
					e.Graphics.FillRectangle(SystemBrushes.ButtonFace, 1, Height - 16, Width - 2, 14);
					GripBounds = new Rectangle(Width - 17, Height - 16, 16, 16);
					GripRenderer.Render(e.Graphics, GripBounds.Location, GripAlignMode.BottomRight);
				}
				else if (CompareResizeMode(PopupResizeMode.TopLeft))
				{
					// Draw grip area at top-left of popup.
					e.Graphics.FillRectangle(SystemBrushes.ButtonFace, 1, 1, Width - 2, 14);
					GripBounds = new Rectangle(1, 0, 16, 16);
					GripRenderer.Render(e.Graphics, GripBounds.Location, GripAlignMode.TopLeft);
				}
				else if (CompareResizeMode(PopupResizeMode.TopRight))
				{
					// Draw grip area at top-right of popup.
					e.Graphics.FillRectangle(SystemBrushes.ButtonFace, 1, 1, Width - 2, 14);
					GripBounds = new Rectangle(Width - 17, 0, 16, 16);
					GripRenderer.Render(e.Graphics, GripBounds.Location, GripAlignMode.TopRight);
				}
			}

			protected override void OnSizeChanged(EventArgs e)
			{
				base.OnSizeChanged(e);

				// When drop-down window is being resized by the user (i.e. not locked),
				// update size of hosted control.
				if (!lockedThisSize)
					RecalculateHostedControlLayout();
			}

			protected void RecalculateHostedControlLayout()
			{
				if (lockedHostedControlSize)
					return;

				lockedThisSize = true;

				// Update size of hosted control.
				var hostedControl = GetHostedControl();
				if (hostedControl != null)
				{
					// Fetch control bounds and adjust as necessary.
					var bounds = hostedControl.Bounds;
					if (CompareResizeMode(PopupResizeMode.TopLeft) || CompareResizeMode(PopupResizeMode.TopRight))
						bounds.Location = new Point(1, 16);
					else
						bounds.Location = new Point(1, 1);

					bounds.Width = ClientRectangle.Width - 2;
					bounds.Height = ClientRectangle.Height - 2;
					if (IsGripShown)
						bounds.Height -= 16;

					if (bounds.Size != hostedControl.Size)
						hostedControl.Size = bounds.Size;
					if (bounds.Location != hostedControl.Location)
						hostedControl.Location = bounds.Location;
				}

				lockedThisSize = false;
			}

			protected void ResizeFromContent(int width)
			{
				if (lockedThisSize)
					return;

				// Prevent resizing hosted control to 1x1 pixel!
				lockedHostedControlSize = true;

				// Resize from content again because certain information was not available before.
				var bounds = Bounds;
				bounds.Size = SizeFromContent(width);

				if (!CompareResizeMode(PopupResizeMode.None))
				{
					if (width > 0 && bounds.Width - 2 > width)
						if (!CompareResizeMode(PopupResizeMode.Right))
							bounds.X -= bounds.Width - 2 - width;
				}

				Bounds = bounds;

				lockedHostedControlSize = false;
			}

			protected Size SizeFromContent(int width)
			{
				var contentSize = Size.Empty;

				refreshSize = false;

				// Fetch hosted control.
				var hostedControl = GetHostedControl();
				if (hostedControl != null)
				{
					if (CompareResizeMode(PopupResizeMode.TopLeft) || CompareResizeMode(PopupResizeMode.TopRight))
						hostedControl.Location = new Point(1, 16);
					else
						hostedControl.Location = new Point(1, 1);
					contentSize = SizeFromClientSize(hostedControl.Size);

					// Use minimum width (if specified).
					if (width > 0 && contentSize.Width < width)
					{
						contentSize.Width = width;
						refreshSize = true;
					}
				}

				// If a grip box is shown then add it into the drop down height.
				if (IsGripShown)
					contentSize.Height += 16;

				// Add some additional space to allow for borders.
				contentSize.Width += 2;
				contentSize.Height += 2;

				return contentSize;
			}

			protected override void WndProc(ref Message m)
			{
				if (!ProcessGrip(ref m, false))
					base.WndProc(ref m);
			}

			private bool OnGetMinMaxInfo(ref Message m)
			{
				var hostedControl = GetHostedControl();
				if (hostedControl != null)
				{
					var minmax = m.LParam.ToStructure<MINMAXINFO>();

					// Maximum size.
					if (hostedControl.MaximumSize.Width != 0)
						minmax.maxTrackSize.Width = hostedControl.MaximumSize.Width;
					if (hostedControl.MaximumSize.Height != 0)
						minmax.maxTrackSize.Height = hostedControl.MaximumSize.Height;

					// Minimum size.
					minmax.minTrackSize = new Size(32, 32);
					if (hostedControl.MinimumSize.Width > minmax.minTrackSize.Width)
						minmax.minTrackSize.Width = hostedControl.MinimumSize.Width;
					if (hostedControl.MinimumSize.Height > minmax.minTrackSize.Height)
						minmax.minTrackSize.Height = hostedControl.MinimumSize.Height;

					Marshal.StructureToPtr(minmax, m.LParam, false);
				}
				return true;
			}

			private bool OnNcHitTest(ref Message m, bool contentControl)
			{
				var location = PointToClient(new Point(LOWORD(m.LParam), HIWORD(m.LParam)));
				var transparent = (IntPtr)HitTestValues.HTTRANSPARENT;

				// Check for simple gripper dragging.
				if (GripBounds.Contains(location))
				{
					if (CompareResizeMode(PopupResizeMode.BottomLeft))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTBOTTOMLEFT;
						return true;
					}
					if (CompareResizeMode(PopupResizeMode.BottomRight))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTBOTTOMRIGHT;
						return true;
					}
					if (CompareResizeMode(PopupResizeMode.TopLeft))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTTOPLEFT;
						return true;
					}
					if (CompareResizeMode(PopupResizeMode.TopRight))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTTOPRIGHT;
						return true;
					}
				}
				else   // Check for edge based dragging.
				{
					var rectClient = ClientRectangle;
					if (location.X > rectClient.Right - 3 && location.X <= rectClient.Right && CompareResizeMode(PopupResizeMode.Right))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTRIGHT;
						return true;
					}
					if (location.Y > rectClient.Bottom - 3 && location.Y <= rectClient.Bottom && CompareResizeMode(PopupResizeMode.Bottom))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTBOTTOM;
						return true;
					}
					if (location.X > -1 && location.X < 3 && CompareResizeMode(PopupResizeMode.Left))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTLEFT;
						return true;
					}
					if (location.Y > -1 && location.Y < 3 && CompareResizeMode(PopupResizeMode.Top))
					{
						m.Result = contentControl ? transparent : (IntPtr)HitTestValues.HTTOP;
						return true;
					}
				}
				return false;
			}

			private bool ProcessGrip(ref Message m, bool contentControl)
			{
				if (ResizeMode != PopupResizeMode.None)
				{
					switch (m.Msg)
					{
						case (int)WindowMessage.WM_NCHITTEST:
							return OnNcHitTest(ref m, contentControl);

						case (int)WindowMessage.WM_GETMINMAXINFO:
							return OnGetMinMaxInfo(ref m);
					}
				}
				return false;
			}
		}
	}
}